package com.qf.listener;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qf.config.DelayedConfig;
import com.qf.constant.CacheConstants;
import com.qf.constant.RabbitMQConstants;
import com.qf.feign.CacheFeign;
import com.qf.model.StandardReport;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.util.Map;

/**
 *
 * 手动ack， 手动确认消息消费成功
 * 推送报告监听器
 * @author 千锋教育
 * @Company http://www.mobiletrain.org/
 * @Version 1.0
 */
@Component
@Slf4j
public class PushReportListener {

    @Autowired
    CacheFeign cacheFeign;


    @Autowired
    RabbitTemplate template;


    //第一次 推送  ， 消费 消息
    @RabbitListener(queues = RabbitMQConstants.TOPIC_PUSH_SMS_REPORT)
    public void consum(StandardReport report, Channel channel, Message message) {
        //1. 获取 clientId
        long clientID = report.getClientID();
        //2. 获取client对象
        Map<String,String> map = cacheFeign.hMGet(CacheConstants.CACHE_PREFIX_CLIENT + clientID);
        //3. 获取 receivestatusurl,(回调地址)，
        String receivestatusurl = map.get("receivestatusurl");
        //4. 获取 isreturnstatus的值， 如果为1，则推送报告给 客户端（回调）
        String isreturnstatus = map.get("isreturnstatus");
        if("1".equalsIgnoreCase(isreturnstatus)) {
            //4.1 发送报告， 判断是否发送成功，如果发送成功，则手动ack。
            //设置第一次发送
            report.setSendCount(1);
            //设置 回调url地址
            report.setReceiveStatusUrl(receivestatusurl);
            //发送报告
            String result = null;
            try {
                result = sendReport(report);
            } catch (Exception e) {
                e.printStackTrace();
            }
            //4.2 确认结果
            isSuccess(report, result, channel, message);

        }
        //5. 如果为0， 则不推送报告（不回调）
        else {
            //在控制台记录日志
            log.info("[接口模块--报告推送] 客户端不需要推送报告！！ report = {}", report);
            //手动ack（手动确认消息）
            try {
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    @Autowired
    RestTemplate restTemplate;

    //发送 报告到 客户端(千锋互联)
    private String sendReport(StandardReport report) {
        //回调 url地址
        String url = report.getReceiveStatusUrl();


        //请求头
        HttpHeaders httpHeaders = new HttpHeaders();
        //请求体数据类型
        httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);

        //把 report对象 转换为json
        ObjectMapper mapper = new ObjectMapper();
        String json = null;
        try {
            json = mapper.writeValueAsString(report);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        /**
         *参数1： body 为 请求体 数据
         * 参数2： header ： 请求头
         */
        HttpEntity httpEntity = new HttpEntity(json , httpHeaders);

        /**
         * 参数1： url路径
         * 参数2： 请求头对象
         * 参数3： 返回值类型
         */
        ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, httpEntity, String.class);
        // body 是 方法的返回值
        return responseEntity.getBody();
    }

    private static final int[] ttl = new int[]{0, 10000, 30000, 50000, 100000};

    //如果发送失败，把消息 发送到延时队列。 手动ack, 如果成功， 手动ack
    private void isSuccess(StandardReport report, String result, Channel channel, Message message) {
        //如果 回调的结果为 success，则认为 发送报告成功
        if("success".equalsIgnoreCase(result)) {
            log.info("[接口模块 -- 发送报告]  发送报告成功 ！！ report = {} ", report);
        }
        //如果不是 success， 发送报告失败， 推送消息 到延时队列（等会儿再推送一次） {
        else {
            log.info("[接口模块 -- 发送报告]  第 {} 次发送报告失败 ！！ report = {} ", report.getSendCount(), report);

            //发送消息到延时队列
            //最多发送5次，如果 小于4次，则再次发送
            if (report.getSendCount() <= 4) {
                //设置发送次数
                report.setSendCount(report.getSendCount() + 1);
                //参数4： 消息的后处理对象
                template.convertAndSend(DelayedConfig.DELAYED_EXCHANGE, "routingKey", report, new MessagePostProcessor() {
                    @Override
                    public Message postProcessMessage(Message message) throws AmqpException {
                        //设置延时 时间
                        message.getMessageProperties().setDelay(ttl[report.getSendCount() - 1]);
                        return message;
                    }
                });
            }

        }
        //无论成功与否，需要手动ack
        try {
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 处理 延时队列的消息（除第一个发送报告，都要走此方法）
     */
    @RabbitListener(queues = DelayedConfig.DELAYED_QUEUE)
    public void consumDelay(StandardReport report ,Channel channel, Message message) {
        //发送报告
        String result = null;
        try {
            result = this.sendReport(report);
        } catch (Exception e){
            e.printStackTrace();
        }
        //处理结果
        isSuccess(report, result, channel, message);
    }
}
